#include "YXJwt.h"
#include "../../include/l8w8jwt/decode.h"
#include "../../include/l8w8jwt/encode.h"
#include "YXBase64.h"
#include "YXConfig.h"
#include "YXConstant.h"
#include "YXLog.h"

#include <limits.h>
#include <string.h>
#include <time.h>


static char *jwt_secret;
static int   jwt_secret_len;
static int   jwt_expire;

static void yx_jwt_builder_free(JwtBuilder *jwt_builder);

JwtBuilder *yx_jwt_create() {
    char  **jwt        = malloc(sizeof(char *));
    size_t *jwt_length = malloc(sizeof(size_t));

    struct l8w8jwt_encoding_params *params = malloc(sizeof(struct l8w8jwt_encoding_params));
    l8w8jwt_encoding_params_init(params);

    params->alg = L8W8JWT_ALG_HS256;

    params->exp = time(NULL) + jwt_expire;

    params->secret_key        = (unsigned char *) jwt_secret;
    params->secret_key_length = jwt_secret_len;

    params->out        = jwt;
    params->out_length = jwt_length;

    JwtBuilder *jwt_builder = malloc(sizeof(JwtBuilder));
    jwt_builder->params     = params;
    jwt_builder->params->additional_payload_claims =
            malloc(sizeof(struct l8w8jwt_claim) * YX_JWT_CLAIMS_DEFAULT_CAPACITY);
    jwt_builder->params->additional_payload_claims_count = 0;
    jwt_builder->claims_capacity                         = YX_JWT_CLAIMS_DEFAULT_CAPACITY;

    return jwt_builder;
}

void yx_jwt_add_claims_int(JwtBuilder *jwt_builder, char *key, int key_len, long value) {
    if (jwt_builder->params->additional_payload_claims_count == jwt_builder->claims_capacity) {
        jwt_builder->claims_capacity *= 2;
        jwt_builder->params->additional_payload_claims =
                realloc(jwt_builder->params->additional_payload_claims,
                        sizeof(struct l8w8jwt_claim) * jwt_builder->claims_capacity);
    }

    // 获取整型的长度
    int value_len = snprintf(NULL, 0, "%ld", value);
    // 把整型转换为字符串
    char *value_str = malloc(value_len);
    sprintf(value_str, "%ld", value);

    jwt_builder->params->additional_payload_claims[jwt_builder->params->additional_payload_claims_count].key = key;
    jwt_builder->params->additional_payload_claims[jwt_builder->params->additional_payload_claims_count].key_length = key_len;
    jwt_builder->params->additional_payload_claims[jwt_builder->params->additional_payload_claims_count].value = value_str;
    jwt_builder->params->additional_payload_claims[jwt_builder->params->additional_payload_claims_count].value_length = value_len - 1;
    jwt_builder->params->additional_payload_claims[jwt_builder->params->additional_payload_claims_count].type = L8W8JWT_CLAIM_TYPE_INTEGER;
    jwt_builder->params->additional_payload_claims_count += 1;
}

void yx_jwt_add_claims_str(JwtBuilder *jwt_builder, char *key, int key_len, char *value) {
    if (jwt_builder->params->additional_payload_claims_count == jwt_builder->claims_capacity) {
        jwt_builder->claims_capacity *= 2;
        jwt_builder->params->additional_payload_claims =
                realloc(jwt_builder->params->additional_payload_claims,
                        sizeof(struct l8w8jwt_claim) * jwt_builder->claims_capacity);
    }

    jwt_builder->params->additional_payload_claims[jwt_builder->params->additional_payload_claims_count].key = key;
    jwt_builder->params->additional_payload_claims[jwt_builder->params->additional_payload_claims_count].key_length = key_len;
    jwt_builder->params->additional_payload_claims[jwt_builder->params->additional_payload_claims_count].value = value;
    jwt_builder->params->additional_payload_claims[jwt_builder->params->additional_payload_claims_count].value_length = strlen(value);
    jwt_builder->params->additional_payload_claims[jwt_builder->params->additional_payload_claims_count].type = L8W8JWT_CLAIM_TYPE_STRING;
    jwt_builder->params->additional_payload_claims_count += 1;
}

char *yx_jwt_generate(JwtBuilder *jwt_builder) {
    if (l8w8jwt_encode(jwt_builder->params) != L8W8JWT_SUCCESS) {
        yx_jwt_builder_free(jwt_builder);
        return NULL;
    }
    char *token = *jwt_builder->params->out;
    yx_jwt_builder_free(jwt_builder);
    return token;
}

static void yx_jwt_builder_free(JwtBuilder *jwt_builder) {
    for (int i = 0; i < jwt_builder->params->additional_payload_claims_count; i++) {
        free(jwt_builder->params->additional_payload_claims[i].value);
    }
    free(jwt_builder->params->additional_payload_claims);
    free(jwt_builder->params->out);
    free(jwt_builder->params->out_length);
    free(jwt_builder->params);
    free(jwt_builder);
}

Claims *yx_jwt_parse(const char *token) {
    struct l8w8jwt_decoding_params params;
    l8w8jwt_decoding_params_init(&params);

    params.alg                     = L8W8JWT_ALG_HS256;
    params.jwt                     = token;
    params.jwt_length              = strlen(token);
    params.verification_key        = (unsigned char *) jwt_secret;
    params.verification_key_length = jwt_secret_len;

    params.validate_exp          = 0;
    params.exp_tolerance_seconds = 60;

    enum l8w8jwt_validation_result validation_result;
    struct l8w8jwt_claim          *l8_w8_jwt_claim;
    size_t                         claims_size;

    int decode_result = l8w8jwt_decode(&params, &validation_result, &l8_w8_jwt_claim, &claims_size);

    if (decode_result == L8W8JWT_SUCCESS && validation_result == L8W8JWT_VALID) {
        Claims *claims = (Claims *) malloc(sizeof(Claims));
        claims->claims = l8_w8_jwt_claim;
        claims->size   = claims_size;
        return claims;
    }

    return NULL;
}

void yx_jwt_free(char *token) { l8w8jwt_free(token); }

long yx_jwt_claims_get_int(Claims *claims, char *key, size_t key_len) {
    struct l8w8jwt_claim *target = l8w8jwt_get_claim(claims->claims, claims->size, key, key_len);
    if (target == NULL)
        return -1;
    return atoi(target->value);
}

char *yx_jwt_claims_get_str(Claims *claims, char *key, size_t key_len) {
    struct l8w8jwt_claim *target = l8w8jwt_get_claim(claims->claims, claims->size, key, key_len);
    if (target == NULL)
        return NULL;
    return target->value;
}

void yx_jwt_claims_free(Claims *claims) {
    l8w8jwt_free_claims(claims->claims, claims->size);
    free(claims);
}

void yx_jwt_set_secret(char *secret, int len) {
    // 对secret进行base64解码
    char *deocded_secret = yx_base64_decode(secret, len);
    jwt_secret           = deocded_secret;
    jwt_secret_len       = strlen(deocded_secret);
}

int yx_jwt_set_expire(int expire) {
    if (expire > INT_MAX) {
        return YX_ERR;
    }
    jwt_expire = expire;
    return YX_OK;
}

char *yx_jwt_get_secret() { return jwt_secret; }

int yx_jwt_get_expire() { return jwt_expire; }

__attribute__((constructor(YX_CONFIG_INIT_PRIORITY + 1))) static void yx_jwt_init() {
    char *secret = YX_JWT_DEAULT_SECRET;
    if (yxConfig.haveProperty("jwt.secret")) {
        secret = yxConfig.getStrProperty("jwt.secret");
    } else {
        logWarning("You would better set {{ jwt.secret }} in config file");
    }
    yx_jwt_set_secret(secret, strlen(secret));


    int expire = YX_JWT_DEAULT_EXPIRE;
    if (yxConfig.haveProperty("jwt.expire")) {
        expire = yxConfig.getIntProperty("jwt.expire");
    }
    if (yx_jwt_set_expire(expire) == YX_ERR) {
        logFatal("jwt.expire must be less than INT_MAX");
    }
}